<script setup lang="ts">
// @ts-ignore
import {
  isString,
  isNumber,
  INJECTION_SYMBOL_PANE_LENGTH_PERCENT,
  INJECTION_SYMBOL_IS_MOVING
} from '@/utils'

const props = defineProps({
  direction: {
    type: String,
    default: 'row'
  },
  // 区域一所占整体百分比
  paneLengthPercent: {
    type: [Number, String],
    default: '50%',
    required: true
  },
  triggerLength: {
    type: [Number, String],
    default: '10px'
  },
  min: {
    type: String
  },
  max: {
    type: String
  }
})

const emit = defineEmits(['update:pane-length-percent'])
const { direction, paneLengthPercent, triggerLength, min, max } = toRefs(props)
provide(INJECTION_SYMBOL_PANE_LENGTH_PERCENT, paneLengthPercent)

// 鼠标距滑动器左侧(上侧)的偏移距离
const triggerLeftOffset = ref<Number>(0)
const splitPaneRef = ref<null | Element>(null)
const isMoving = ref(false)
provide(INJECTION_SYMBOL_IS_MOVING, isMoving)

const processedPaneLengthPercent = computed(() => {
  const percent = paneLengthPercent.value
  if (!isNumber(percent) && !isString(percent)) {
    throw new Error('paneLengthPercent value must be type of string or number')
  }

  if (isNumber(percent)) {
    return `${percent}%`
  }

  if (isString(percent)) {
    if (percent.endsWith('%')) {
      return percent
    } else {
      throw new Error('paneLengthPercent value must be type of string and end up with %')
    }
  }
})
const processedTriggerLength = computed(() => {
  const length = triggerLength.value
  if (!isNumber(length) && !isString(length)) {
    throw new Error('triggerLength value must be type of string or number')
  }

  if (isNumber(length)) {
    return `${length}px`
  }

  if (isString(length)) {
    if (length.endsWith('px')) {
      return length
    } else {
      throw new Error('triggerLength value must be type of string and end up with px')
    }
  }

  return '0px'
})
const paneLengthValue = computed(
  () =>
    `calc(${processedPaneLengthPercent.value} - ${parseFloat(processedTriggerLength.value) / 2}px)`
)
const processedMin = computed(() => {
  const m = min!.value
  if (m) {
    if (isString(m)) {
      if (m.endsWith('%')) {
        return parseFloat(m)
      } else {
        throw new Error('min value must be type of string and end up with %')
      }
    }
  }
  return m
})
const processedMax = computed(() => {
  const m = max!.value
  if (m) {
    if (isString(m)) {
      if (m.endsWith('%')) {
        return parseFloat(m)
      } else {
        throw new Error('max value must be type of string and end up with %')
      }
    }
  }
  return m
})

const onMouseDown = (e: MouseEvent) => {
  // 事件绑定
  document.addEventListener('mousemove', onMouseMove)
  document.addEventListener('mouseup', onMouseUp)

  // 计算逻辑始终认为鼠标在滑动器的正中间
  if (direction.value === 'row') {
    // Event.srcElement 是标准的 Event.target 属性的一个别名。 该特性是非标准的，请尽量不要在生产环境中使用它！
    triggerLeftOffset.value = e.pageX - e.target.getBoundingClientRect().left
  } else if (direction.value === 'column') {
    triggerLeftOffset.value = e.pageY - e.target.getBoundingClientRect().top
  }
}
const onMouseMove = (e: MouseEvent) => {
  const clientRect = splitPaneRef.value?.getBoundingClientRect()
  let paneLengthPercent = 0

  if (direction.value === 'row') {
    const offset =
      e.pageX - clientRect.left - triggerLeftOffset.value + parseFloat(triggerLength.value) / 2
    paneLengthPercent = (offset / clientRect.width) * 100
  } else if (direction.value === 'column') {
    const offset =
      e.pageY - clientRect.top - triggerLeftOffset.value + parseFloat(triggerLength.value) / 2
    paneLengthPercent = (offset / clientRect.width) * 100
  }

  if (processedMin.value && paneLengthPercent < processedMin.value) {
    paneLengthPercent = processedMin.value
  }
  if (processedMax.value && paneLengthPercent > processedMax.value) {
    paneLengthPercent = processedMax.value
  }

  isMoving.value = true
  emit('update:pane-length-percent', `${paneLengthPercent}%`)
}
const onMouseUp = () => {
  isMoving.value = false
  document.removeEventListener('mousemove', onMouseMove)
  document.removeEventListener('mouseup', onMouseUp)
}
</script>

<template>
  <div
    :class="['split-pane', direction === 'row' ? 'row-direction' : 'column-direction']"
    ref="splitPaneRef"
  >
    <div :class="['pane pane-one', direction === 'row' ? 'row-direction' : 'column-direction']">
      <slot name="pane-one"></slot>
    </div>
    <div
      :class="['pane-trigger-wrapper', direction === 'row' ? 'row-direction' : 'column-direction']"
      @mousedown="onMouseDown"
    >
      <slot name="pane-trigger">
        <div class="pane-trigger"></div>
      </slot>
    </div>
    <div :class="['pane pane-two', direction === 'row' ? 'row-direction' : 'column-direction']">
      <slot name="pane-two"></slot>
    </div>
  </div>
</template>

<style scoped lang="scss">
.split-pane {
  display: flex;
  width: 100%;
  height: 100%;

  &.row-direction {
    flex-direction: row;
  }

  &.column-direction {
    flex-direction: column;
  }

  .pane-one {
    &.row-direction {
      width: v-bind(paneLengthValue);
      height: 100%;
    }

    &.column-direction {
      width: 100%;
      height: v-bind(paneLengthValue);
    }
  }

  .pane-trigger-wrapper {
    // 在拖动过程中，如果区域中有文字内容可能会出现选中文字的情况，给滑动器添加禁止选中效果。
    user-select: none;
    // background: palegoldenrod;
    /*拖拽区鼠标悬停样式*/
    &:hover {
      color: orange;
    }

    &.row-direction {
      height: 100%;
      cursor: col-resize;
      .pane-trigger,
      > * {
        width: v-bind(processedTriggerLength);
        height: 100%;
      }
    }

    &.column-direction {
      width: 100%;
      cursor: row-resize;
      .pane-trigger,
      > * {
        width: 100%;
        height: v-bind(processedTriggerLength);
      }
    }
  }

  .pane-two {
    flex: 1;

    &.row-direction {
      width: 0;
      height: 100%;
    }

    &.column-direction {
      width: 100%;
      height: 0;
    }
  }
}
</style>
